#ifndef RP2CAMERA_H
#define RP2CAMERA_H

//#include "Rp2Renderer.h"
#include "Rp2Object.h"
#include "Rp2Vector3.h"
#include "Rp2Matrix4.h"

namespace Rp2
{

class Renderer;

class Camera : public Object
{
public:
	Camera();
	virtual ~Camera();

	// The camera frame is always in world coordinates.
	// Order is (R, U, D), left-handed rule.
	//   default location  E = (0,0,0)
	//   default right	   R = (1,0,0)
	//   default up        U = (0,1,0)
	//   default direction D = (0,0,1)

	void SetFrame (const Vector3f& rkLocation, const Vector3f& rkRVector,
		const Vector3f& rkUVector, const Vector3f& rkDVector);
	void SetLocation (const Vector3f& rkLocation);
	void SetAxes(const Vector3f& rkRVector, const Vector3f& rkUVector,
		const Vector3f& rkDVector);
	void LookAt(const Vector3f& rkLookAt);
	void LocationLookAt(const Vector3f& rkLocation, const Vector3f& rkLookAt);
	Vector3f GetLocation () const;
	Vector3f GetRVector() const;
	Vector3f GetUVector() const;
	Vector3f GetDVector() const;
	
	enum
	{
		VF_RMIN     = 0,  // left
		VF_RMAX     = 1,  // right
		VF_UMIN     = 2,  // bottom
		VF_UMAX     = 3,  // top
		VF_DMIN     = 4,  // near
		VF_DMAX     = 5,  // far
		VF_QUANTITY = 6
	};
	// Set a symmetric view frustum (umin = -umax, rmin = -rmax) using a field
	// of view in the "up" direction and an aspect ratio "width/height".  This
	// call is the equivalent of gluPerspective in OpenGL.  As such, the field
	// of view in this function must be specified in degrees and be in the
	// interval (0,180)
	void SetFrustum (float fFovDegrees, float fAspectRatio, float fDMin, float fDMax);
    // Set the view frustum.  The interval [rmin,rmax] is measured in the
    // right direction R.  These are the "left" and "right" frustum values.
    // The interval [umin,umax] is measured in the up direction U.  These are
    // the "bottom" and "top" values.  The interval [dmin,dmax] is measured
    // in the view direction D.  These are the "near" and "far" values.
    void SetFrustum (float fRMin, float fRMax, float fUMin, float fUMax,
        float fDMin, float fDMax);
    // Set all the view frustum values simultaneously.  TO DO:  Currently,
    // these must be in the order compatible with the VF_* constants.  That
    // is:  dmin, dmax, umin, umax, rmin, rmax.  This is confusing because
    // the previous SetFrustum uses a different order.  Make them consistent.
    void SetFrustum (const float* afFrustum);

	void GetFrustum (float& rfRMin,float& rfRMax,float& rfUMin,float& rfUMax,float& rfDMin,float& rfDMax);
    // Get all the view frustum values simultaneously.
    const float* GetFrustum () const;

    // The viewport is contained in [0,W]x[0,H], where W is the window width
    // and H is the window height.  If the input width and height are zero,
    // the integer values for the viewport are all set to zero and the
    // floating-point viewport is [0,1]x[0,1].
    void SetViewport (int iLeft, int iRight, int iTop, int iBottom,
        int iWidth, int iHeight);

    // If the currently stored width and height are positive, then this call
    // returns all the stored integer values.  If the width and height are
    // zero, the returned values are computed
    void GetViewport (int& riLeft, int& riRight, int& riTop, int& riBottom,
        int& riWidth, int& riHeight);

    // Allow for orthogonal cameras as well as perspective cameras.  The
    // default is perspective (value is 'true').  Set to 'false' for an
    // orthogonal camera.  TO DO.  Stream this member.
    bool Perspective;

	// Get associated matrices
	Matrix4f GetViewMatrix();
	Matrix4f GetProjMatrix();

protected:
	// Camera frame
	Vector3f m_kLocation, m_kDVector, m_kUVector, m_kRVector;
	
	// Frustum
	float m_afFrustum[VF_QUANTITY];
	// viewport in integer values
	int m_iWidth, m_iHeight;
	int m_iViewPortL, m_iViewPortR, m_iViewPortB, m_iViewPortT;

	// viewport in float [0,1]
	float m_fViewPortL, m_fViewPortR, m_fViewPortB, m_fViewPortT;

	// depth range in [0,1]
	float m_fNear, m_fFar;

	friend class Renderer;
	// associated renderer
	Renderer* m_pkRenderer;
};

#include "Rp2Camera.inl"
typedef Pointer<Camera> CameraPtr;

}

#endif